# Grammar declaration to be used by racc 1.4.5 ot create a parser
# for the programming language Vectorinox

class Parser

# Declaration of the precedences that have to be followed while parsing the language

  prechigh
    nonassoc TkElse
    nonassoc IF
    right TkExp
    left TkProd TkDiv TkMod TkPoint
    left TkSum TkMinus
    left TkLessT TkGreatT TkLessET TkGreatET TkEqual TkNEqual
    left TkAnd
    left TkOr
  preclow

# Declaration of all the terminals and auxilary non-terminals that are used in the grammar

  token
    IF
    TkNum
    TkVec
    TkMat
    TkOf
    TkType
    TkAs
    TkBegin
    TkEnd
    TkVars
    TkIf
    TkThen
    TkElse
    TkWhile
    TkDo
    TkForeach
    TkIn
    TkRead
    TkWrite
    TkReturn
    TkDefine
    TkZeroes
    TkRange
    TkEye
    TkTrue
    TkFalse
    TkCnum
    TkCString
    TkVar
    TkComment
    TkSum
    TkMinus
    TkDiv
    TkExp
    TkProd
    TkLessT
    TkGreatT
    TkLessET
    TkGreatET
    TkEqual
    TkNEqual
    TkPoint
    TkLSBrack
    TkRSBrack
    TkAsig
    TkLParenth
    TkRParenth
    TkMod
    TkLBrack
    TkRBrack
    TkDollar
    TkAt
    TkNot
    TkTrasp
    TkColon
    TkComma
    TkSemiColon
    TkAnd
    TkOr

# Rules that represent the grammar that parses the programming language Vectorinox
  rule

# Initial rule
    target: S { result = val[0] }

# Main body structure of a Vectorinox program
    S:
        ldeclfunc instwor { result = ASTMain.new(val[0], val[1]) }
      | instwor { result = ASTMain.new(nil, val[0]) }

# List of function declarations
    ldeclfunc:
        ldeclfunc declfunc { result = ASTListComma.new(val[1], val[0].children) }
      | declfunc { result = ASTListComma.new(val[0], []) }

# A single function declaration
    declfunc:
        TkDefine TkVar TkLParenth lparame TkRParenth TkOf TkType type TkAs inst { result = ASTFunc.new(ASTExpression.new(val[1], nil), val[3] == nil ? [] : val[3].children, val[7], val[9]) }

# List of parameters that can be passed to a function, including the lambda-rule
    lparame:
        /* Empty paramters */
      | lparam { result = val[0] }

# List of parameters that can be passed to a function, not including the lambda-rule
    lparam:
        lparam TkComma param { result = ASTListComma.new(val[2], val[0].children) }
      | param { result = ASTListComma.new(val[0], []) }

# A single input parameter declaration for a function declaration
    param:
        TkVar TkColon type { result = ASTVar.new(val[2], [val[0]]) }

# Instruction to form a begin-end blocks without return statements
    instwor:
        TkBegin decl linstwor TkEnd { result = ASTBlock.new(val[1], val[2].children) }
      | TkWrite lcexpr { result = ASTStatement.new(val[0], val[1].children) }
      | TkRead TkVar { result = ASTStatement.new(val[0], [ASTVar.new(nil, [val[1]])]) }
      | TkWhile expr TkDo instwor { result = ASTStatement.new(val[0], [val[1], val[3]]) }
      | assig TkAsig expr { result = ASTStatement.new(val[1], [val[0], val[2]]) }
      | TkForeach TkVar TkIn expr TkDo instwor { result = ASTStatement.new(val[0], [ASTVar.new(nil, [val[1]]), val[3], val[5]]) }
      | TkIf expr TkThen instwor =IF { result = ASTStatement.new(val[0], [val[1], val[3]]) }
      | TkIf expr TkThen instwor TkElse instwor { result = ASTStatement.new(val[0], [val[1], val[3], val[5]]) }

# List of semi-colon separated instructions which exclude return statements
    linstwor:
        linstwor TkSemiColon instwor { result = ASTListSemiColon.new(val[2], val[0].children) }
      | instwor { result = ASTListSemiColon.new(val[0], []) }

# Instruction to form a begin-end blocks
    inst:
        TkBegin decl linst TkEnd { result = ASTBlock.new(val[1], val[2].children) }
      | TkWrite lcexpr { result = ASTStatement.new(val[0], val[1].children) }
      | TkRead TkVar { result = ASTStatement.new(val[0], [ASTVar.new(nil, [val[1]])]) }
      | TkReturn expr { result = ASTStatement.new(val[0], [val[1]]) }
      | TkWhile expr TkDo inst { result = ASTStatement.new(val[0], [val[1], val[3]]) }
      | assig TkAsig expr { result = ASTStatement.new(val[1], [val[0], val[2]]) }
      | TkForeach TkVar TkIn expr TkDo inst { result = ASTStatement.new(val[0], [ASTVar.new(nil, [val[1]]), val[3], val[5]]) }
      | TkIf expr TkThen inst =IF { result = ASTStatement.new(val[0], [val[1], val[3]]) }
      | TkIf expr TkThen inst TkElse inst { result = ASTStatement.new(val[0], [val[1], val[3], val[5]]) }

# List of semi-colon separated instructions
    linst:
        linst TkSemiColon inst { result = ASTListSemiColon.new(val[2], val[0].children) }
      | inst { result = ASTListSemiColon.new(val[0], []) }

# Variable declaration block, including empty declarations
    decl:
        /* Empty declarations */
      | TkVars ldecllvar { result = val[1] }

# List of semi-colon separated variable declarations
    ldecllvar:
        ldecllvar TkSemiColon decllvar { result = ASTListSemiColon.new(val[2], val[0].children) }
      | decllvar { result = ASTListSemiColon.new(val[0], []) }

# Declaration of variables of the same type
    decllvar:
        lvar TkColon type { result = ASTVar.new(val[2], val[0]) }

# List of comma separated variables
    lvar:
        lvar TkComma TkVar { result = val[0].push(val[2]) }
      | TkVar { result = [val[0]] }

# Types accepted by Vectorinox
    type:
        TkNum { result = val[0] }
      | TkVec { result = val[0] }
      | TkMat { result = val[0] }

# Valid assignable expressions
    assig:
        TkVar { result = ASTAssignable.new(val[0], nil) }
      | TkVar TkLSBrack range TkRSBrack { result = ASTAssignable.new(val[0], ASTAccessVec.new(ASTExpression.new(val[0], nil), val[2])) }
      | TkVar TkLSBrack range TkComma range TkRSBrack { result = ASTAssignable.new(val[0], ASTAccessMat.new(ASTExpression.new(val[0], nil), val[2], val[4])) }

# Valid expresions using binary operators and parenthesis
    expr:
        expr TkSum expr { result = ASTExpression.new(val[1], [val[0], val[2]]) }
      | expr TkMinus expr { result = ASTExpression.new(val[1], [val[0], val[2]]) }
      | expr TkProd expr { result = ASTExpression.new(val[1], [val[0], val[2]]) }
      | expr TkPoint expr { result = ASTExpression.new(val[1], [val[0], val[2]]) }
      | expr TkMod expr { result = ASTExpression.new(val[1], [val[0], val[2]]) }
      | expr TkDiv expr { result = ASTExpression.new(val[1], [val[0], val[2]]) }
      | expr TkExp expr { result = ASTExpression.new(val[1], [val[0], val[2]]) }
      | expr TkLessT expr { result = ASTBool.new(val[1], [val[0], val[2]]) }
      | expr TkGreatT expr { result = ASTBool.new(val[1], [val[0], val[2]]) }
      | expr TkLessET expr { result = ASTBool.new(val[1], [val[0], val[2]]) }
      | expr TkGreatET expr { result = ASTBool.new(val[1], [val[0], val[2]]) }
      | expr TkEqual expr { result = ASTBool.new(val[1], [val[0], val[2]]) }
      | expr TkNEqual expr { result = ASTBool.new(val[1], [val[0], val[2]]) }
      | expr TkAnd expr { result = ASTBool.new(val[1], [val[0], val[2]]) }
      | expr TkOr expr { result = ASTBool.new(val[1], [val[0], val[2]]) }
      | exprb { result = val[0] }

# Valid expressions using left unary operators
    exprb:
        TkMinus exprb { result = ASTExpression.new(val[0], [val[1]]) }
      | TkDollar exprb { result = ASTExpression.new(val[0], [val[1]]) }
      | TkAt exprb { result = ASTExpression.new(val[0], [val[1]]) }
      | TkNot exprb { result = ASTBool.new(val[0], [val[1]]) }
      | exprc { result = val[0] }

# Valid expressions using right operators
    exprc:
        exprc TkTrasp { result = ASTExpression.new(val[1], [val[0]]) }
      | exprc TkLSBrack range TkRSBrack { result = ASTAccessVec.new(val[0], val[2]) }
      | exprc TkLSBrack range TkComma range TkRSBrack { result = ASTAccessMat.new(val[0], val[2], val[4]) }
      | exprd { result = val[0] }

# Valid expressions using function calls, constant matrixes, reserved words, constants and variables
    exprd:
        funcname TkLParenth lcexpr TkRParenth { result = ASTFuncCall.new(val[0], val[2].children) }
      | funcname TkLParenth TkRParenth { result = ASTFuncCall.new(val[0], []) }
      | TkLBrack lsexpr TkRBrack { result = ASTMat.new(val[1]) }
      | TkLBrack TkRBrack { result = ASTMat.new(nil) }
      | TkVar { result = ASTExpression.new(val[0], nil) }
      | TkCnum { result = ASTExpression.new(val[0], nil) }
      | TkCString { result = ASTExpression.new(val[0], nil) }
      | TkTrue { result = ASTBool.new(val[0], nil) }
      | TkFalse { result = ASTBool.new(val[0], nil) }
        # This expression can start over
      | TkLParenth expr TkRParenth { result = val[1] }

# List of comma separated expressions
    lcexpr:
        lcexpr TkComma expr { result = ASTListComma.new(val[2], val[0].children) }
      | expr { result = ASTListComma.new(val[0], []) }

# List of semi-colon separated expressions
    lsexpr:
        lsexpr TkSemiColon lcexpr { result = ASTListSemiColon.new(val[2], val[0].children) }
      | lcexpr { result = ASTListSemiColon.new(val[0], []) }

# Accepted range formats
    range:
        expr { result = ASTRange.new(val[0], val[0]) }
      | expr TkColon expr { result = ASTRange.new(val[0], val[2]) }
      | TkColon expr { result = ASTRange.new(nil, val[1]) }
      | expr TkColon { result = ASTRange.new(val[0], nil) }
      | TkColon { result = ASTRange.new(nil, nil) }

# Accepted function identifiers
    funcname:
        TkVar { result = val[0] }
      | TkEye { result = val[0]  }
      | TkRange { result = val[0] }
      | TkZeroes  { result = val[0] }

# User code that will be created along with the code generated by racc 1.4.5 when
# creating the parser. Includes the definitions of the methods parse(elems), next_token(),
# and on_error().

---- header
require 'parser/AST.rb'
require 'parser/ASTVar.rb'
require 'parser/ASTStatement.rb'
require 'parser/ASTExpression.rb'
require 'parser/ASTBlock.rb'
require 'parser/ASTBool.rb'
require 'parser/ASTAssignable.rb'
require 'parser/ASTAccessVec.rb'
require 'parser/ASTAccessMat.rb'
require 'parser/ASTFunc.rb'
require 'parser/ASTFuncCall.rb'
require 'parser/ASTListComma.rb'
require 'parser/ASTListSemiColon.rb'
require 'parser/ASTMain.rb'
require 'parser/ASTMat.rb'
require 'parser/ASTRange.rb'
---- inner
  
  def parse(elems)
    @q = elems
    do_parse
  end

  def next_token
    @q.shift
  end

  def on_error(error_token_id, error_value, value_stack)
    puts "Caracter no esperado:\n"
    puts "error_token_id: " + error_token_id.to_s
    puts "error_value: " + error_value.to_s
    puts "value_stack:\n"
    i = 0
    for e in value_stack
      puts e.class.name
      puts i.to_s + ": " + ((e.kind_of?AST) ? e.class.name : e.to_s)
      i += 1
    end
  end
